package com.juyu.miliao.common.dataSource.tool.distributeOperate;

import com.juyu.miliao.common.dataSource.MultipleDataSourceParser;
import com.juyu.miliao.common.dataSource.key.DataSourceKey;
import com.juyu.miliao.common.dataSource.key.DataSourceKeyEvaluator;
import com.juyu.miliao.common.dataSource.tool.ArgsUtil;
import com.juyu.miliao.common.dataSource.tool.StrategyParams;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.IntStream;

import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;

@Aspect
public class DistributeOperateAspect {
    private final DataSourceKey dataSourceKey;
    private final DataSourceKeyEvaluator dataSourceKeyEvaluator;
    private final MultipleDataSourceParser multipleDataSourceParser;
    private final DistributeResultHandlers resultHandlers;
    private final ExecutorService executor;
    private final LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();

    public DistributeOperateAspect(DataSourceKey dataSourceKey, DataSourceKeyEvaluator dataSourceKeyEvaluator,
                                   MultipleDataSourceParser multipleDataSourceParser, DistributeResultHandlers resultHandlers) {
        this.dataSourceKey = dataSourceKey;
        this.dataSourceKeyEvaluator = dataSourceKeyEvaluator;
        this.multipleDataSourceParser = multipleDataSourceParser;
        this.resultHandlers = resultHandlers;
        final int availableProcessors = Runtime.getRuntime().availableProcessors();
        this.executor = new ThreadPoolExecutor(availableProcessors, availableProcessors, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new ThreadFactory() {
            private final AtomicLong counter = new AtomicLong();

            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r);
                thread.setName("distribute-operate-thread-" + counter.incrementAndGet());
                thread.setDaemon(true);
                return thread;
            }

        });
    }

    @Pointcut("@annotation(com.juyu.miliao.common.dataSource.tool.distributeOperate.DistributeOperate)")
    public void inDistributeOperateAnnotation() {
    }

    @SuppressWarnings({"unchecked"})
    @Around("inDistributeOperateAnnotation()")
    public Object operate(ProceedingJoinPoint pjp) throws Throwable {
        //创建spel表达式上下文
        Object[] args = pjp.getArgs();
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        EvaluationContext evaluationContext = createEvaluationContext(args, parameterNameDiscoverer.getParameterNames(method));
        DistributeOperate annotation = method.getAnnotation(DistributeOperate.class);
        //创建分库执行结果处理器
        DistributeResultHandler resultHandler;
        if (annotation.resultHandler().equals("")) {    //通过返回结果使用内置的处理器
            resultHandler = parseResultHandlerByReturnType(method.getReturnType()).orElseThrow(() -> new RuntimeException("无法从返回结果推断结果处理方式，需要明确指定resultHandler"));
        } else { //如果不是内置的处理器则使用自定义的处理器
            resultHandler = parseInternalResultHandler(annotation.resultHandler())
                    .orElseGet(() -> parseCustomResultHandler(annotation.resultHandler(), evaluationContext));
        }
        //构建分库执行处理器
        DistributeOperateProcessor processor = new DistributeOperateProcessor(dataSourceKey, resultHandler, executor);
        //如果明确指定了数据源名称则遍历分库执行查询
        if (StringUtils.isNotBlank(annotation.ds())) {
            multipleDataSourceParser.getSubDataSourceNames(annotation.ds()).stream()
                    .forEach(key -> {
                        processor.addJob(key, pjp, args);
                    });
        } else { //根据策略确定要查询的分库
            StrategyParams strategyParams = new StrategyParams();
            new SpelExpressionParser().parseExpression(annotation.value()).getValue(evaluationContext, strategyParams);
            ((Collection<Object>) strategyParams.getParam()).stream()
                    .collect(
                            groupingBy(
                                    obj -> dataSourceKeyEvaluator.strategy(strategyParams.getStrategy(), obj),
                                    toList()
                            )
                    ).forEach((key, list) -> {
                processor.addJob(key, pjp, ArgsUtil.replaceArgs(args, strategyParams.getParam(), list));
            });
        }
        return processor.get();
    }

    /**
     * 没有明确指定resultHandler参数的情况下调用，通过返回类型判断处理器
     *
     * @param returnType
     * @return
     */
    private Optional<DistributeResultHandler> parseResultHandlerByReturnType(Class<?> returnType) {
        if (returnType.equals(int.class) || returnType.equals(Integer.class)) {
            return Optional.of(DistributeResultHandlers.intSum());
        } else if (returnType.equals(long.class) || returnType.equals(Long.class)) {
            return Optional.of(DistributeResultHandlers.longSum());
        } else if (returnType.equals(void.class)) {
            return Optional.of(DistributeResultHandlers.noResult());
        } else if (Collection.class.isAssignableFrom(returnType)) {
            return Optional.of(DistributeResultHandlers.appendCollection());
        }
        return Optional.empty();
    }

    /**
     * 解析自定义的DistributeResultHandler
     *
     * @param annotation
     * @param evaluationContext
     * @return
     */
    private DistributeResultHandler parseCustomResultHandler(String expression, EvaluationContext evaluationContext) {
        return new SpelExpressionParser().parseExpression(expression).getValue(evaluationContext, resultHandlers, DistributeResultHandler.class);
    }

    /**
     * 解析内置的DistributeResultHandler
     *
     * @param annotation
     * @return
     */
    private Optional<DistributeResultHandler> parseInternalResultHandler(String expression) {
        DistributeResultHandler resultHandler = null;
        if ("intSum()".equals(expression))
            resultHandler = DistributeResultHandlers.intSum();
        else if ("longSum()".equals(expression))
            resultHandler = DistributeResultHandlers.longSum();
        else if ("appendCollection()".equals(expression))
            resultHandler = DistributeResultHandlers.appendCollection();
        return Optional.ofNullable(resultHandler);
    }

    private EvaluationContext createEvaluationContext(Object[] args, String[] parameterNames) {
        StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
        IntStream.range(0, parameterNames.length).forEach(i -> {
            evaluationContext.setVariable(parameterNames[i], args[i]);
        });
        return evaluationContext;
    }

}
